package org.jaaksi.svcmanager;

import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

/**
 * 利用ContentProvider实现同步Binder
 */
public class ServiceManager {
  private static final String TAG = "ServiceManager";
  static ServiceChannelImpl sServiceChannel = new ServiceChannelImpl();
  static BinderCursor sCursor = BinderCursor.create(sServiceChannel);

  private static Map<String, IServiceChannel> sServiceMap;

  static {
    sServiceMap = Collections.synchronizedMap(new HashMap<String, IServiceChannel>());
  }

  public static void addService(Context context, String serviceName, IBinder binder) {
    IServiceChannel serverChannel =
      getServerChannel(context, context.getApplicationInfo().packageName);

    if (serverChannel == null) return;
    try {
      serverChannel.addService(serviceName, binder);
    } catch (RemoteException e) {
      e.printStackTrace();
    }
  }

  /**
   * @param packageName 要跨进程访问的app的包名，为了拿到contentprovider的名字
   */
  public static IBinder getService(Context context, String serviceName, String packageName) {
    IServiceChannel serverChannel = getServerChannel(context, packageName);
    if (serverChannel == null) return null;

    try {
      return serverChannel.getService(serviceName);
    } catch (RemoteException e) {
      e.printStackTrace();
    }
    return null;
  }

  static Uri getServiceChannelUri(String packageName) {
    return Uri.parse("content://" + packageName + BinderProvider.AUTHORITY_SUFFIX);
  }

  /**
   * todo 必须根据包名取。其实就是要保证get和add的uri相同
   * 通过contentProvider获取binder
   */
  static IServiceChannel getServerChannel(Context context, String packageName) {
    if (sServiceMap != null) {
      IServiceChannel iServiceChannel = sServiceMap.get(packageName);
      if (iServiceChannel != null
        && iServiceChannel.asBinder().isBinderAlive()
        && iServiceChannel.asBinder().pingBinder()) {
        return iServiceChannel;
      }
    }

    if (context == null) {
      return null;
    }

    IServiceChannel serviceChannel = null;
    Cursor cursor = null;
    try {
      cursor = context.getContentResolver()
        .query(getServiceChannelUri(packageName), null, null, null, null);
      IBinder binder = BinderCursor.getBinder(cursor);
      serviceChannel = IServiceChannel.Stub.asInterface(binder);
      sServiceMap.put(packageName, serviceChannel);
    } catch (Exception e) {
      Log.e(TAG, "Error fetching service manager binder object using provider: ", e);
    } finally {
      if (cursor != null) {
        try {
          cursor.close();
        } catch (Exception e) {
          Log.e(TAG, "Error closing cursor: ", e);
        }
      }
    }
    return serviceChannel;
  }
}
